package com.bigShen.mybatis.official;

import com.bigShen.mybatis.official.dao.InstrumentInfoMapper;
import com.bigShen.mybatis.official.entity.InstrumentInfo;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author : lei.shen
 * @date : 2018/12/3
 * @desc : 批量插入的方式
 *   myBatis 单笔插入;  {@link org.apache.ibatis.session.ExecutorType#SIMPLE}
 *   myBatis 批量插入;  {@link org.apache.ibatis.session.ExecutorType#BATCH}
 *   sql 批量插入 :  sql (for each)
 */
public class MyBatisBatchMain {

    private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSSS");

    public static final SqlSessionFactory getSqlSessionFactory() throws IOException {

        String resource = "mybatis-config.xml";
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(resource), "development");
        return sqlSessionFactory;
    }

    public static void main(String[] args) throws IOException {

        Integer size = 50000;
        Integer batchSize = 1000;

        /**
         * insert 1000 one by one start : 2018-12-04 16:03:07 0870
         * insert 1000 one by one end : 2018-12-04 16:03:09 0276
         * insert 1000 one by one cost: 1406 ms
         * insert 1000 myBatis batch start : 2018-12-04 16:03:09 0830
         * insert 1000 myBatis batch end : 2018-12-04 16:03:09 0944
         * insert 1000 myBatis batch cost: 114 ms
         * insert 1000 sql batch start : 2018-12-04 16:03:10 0457
         * insert 1000 sql batch end : 2018-12-04 16:03:11 0302
         * insert 1000 sql batch cost: 845 ms
         */
        /**
         * insert 20000 one by one start : 2018-12-04 15:58:04 0257
         * insert 20000 one by one end : 2018-12-04 15:58:14 0847
         * insert 20000 one by one cost: 10590
         * insert 20000 myBatis batch start : 2018-12-04 15:58:16 0611
         * insert 20000 myBatis batch end : 2018-12-04 15:58:17 0441
         * insert 20000 myBatis batch cost: 830
         * insert 20000 sql batch start : 2018-12-04 15:58:19 0789
         * insert 20000 sql batch end : 2018-12-04 15:58:26 0395
         * insert 20000 sql batch cost: 6606
         */
        /**
         * insert 50000 one by one start : 2018-12-04 16:04:14 0434
         * insert 50000 one by one end : 2018-12-04 16:04:35 0300
         * insert 50000 one by one cost: 20866 ms
         * insert 50000 myBatis batch start : 2018-12-04 16:04:39 0712
         * insert 50000 myBatis batch end : 2018-12-04 16:04:41 0195
         * insert 50000 myBatis batch cost: 1483 ms
         * insert 50000 sql batch start : 2018-12-04 16:04:47 0599
         * insert 50000 sql batch end : 2018-12-04 16:04:58 0592
         * insert 50000 sql batch cost: 10993 ms
         */
        insertOneByOne(size);

        insertBatchByMybatis(size, batchSize);

        insertBatchBySql(size, batchSize);

    }

    private static void insertOneByOne(Integer size) {

        SqlSession sqlSession = null;
        List<InstrumentInfo> instrumentInfos = new ArrayList<InstrumentInfo>();

        try {

            sqlSession = getSqlSessionFactory().openSession(ExecutorType.SIMPLE, Boolean.FALSE);
            InstrumentInfoMapper instrumentInfoMapper = sqlSession.getMapper(InstrumentInfoMapper.class);
            InstrumentInfo instrumentInfoSource = instrumentInfoMapper.selectByPrimaryKey("111709220", "SPT_BD", "X_CNBD");

            initInstrumentListInfo(instrumentInfos, size, instrumentInfoSource);

            Long start = System.currentTimeMillis();
            System.out.println("insert " + size + " one by one start : " + SIMPLE_DATE_FORMAT.format(new Date()));
            for (InstrumentInfo instrumentInfo : instrumentInfos) {
                instrumentInfoMapper.insert(instrumentInfo);
            }
            Long end = System.currentTimeMillis();
            System.out.println("insert " + size + " one by one end : " + SIMPLE_DATE_FORMAT.format(new Date()));
            System.out.println("insert " + size + " one by one cost: " + (end - start) + " ms");

        } catch (Exception e) {
            System.out.println(getStackTrace(e));
        } finally {
            sqlSession.rollback();
            sqlSession.close();
        }

    }

    private static void initInstrumentListInfo(List<InstrumentInfo> instrumentInfos, Integer size, InstrumentInfo instrumentInfoSource) throws CloneNotSupportedException {
        for (int i = 0; i < size; i++) {
            InstrumentInfo deepClone = instrumentInfoSource.clone();
            //deepClone.setiCode(String.format("%s_%s", deepClone.getiCode(), i));
            deepClone.setiCode(String.valueOf(i));
            deepClone.setaType("BatchOne");
            deepClone.setmType("BatchOne");
            instrumentInfos.add(deepClone);
        }
    }

    private static void insertBatchByMybatis(Integer size, Integer batchSize) {


        SqlSession sqlSession = null;
        List<InstrumentInfo> instrumentInfos = new ArrayList<InstrumentInfo>();

        try {

            sqlSession = getSqlSessionFactory().openSession(ExecutorType.BATCH, Boolean.FALSE);
            InstrumentInfoMapper instrumentInfoMapper = sqlSession.getMapper(InstrumentInfoMapper.class);
            InstrumentInfo instrumentInfoSource = instrumentInfoMapper.selectByPrimaryKey("111709220", "SPT_BD", "X_CNBD");

            initInstrumentListInfo(instrumentInfos, size, instrumentInfoSource);

            Long start = System.currentTimeMillis();
            System.out.println("insert " + size + " myBatis batch start : " + SIMPLE_DATE_FORMAT.format(new Date()));
            for (InstrumentInfo instrumentInfo : instrumentInfos) {
                instrumentInfoMapper.insert(instrumentInfo);
            }
            Long end = System.currentTimeMillis();
            System.out.println("insert " + size + " myBatis batch end : " + SIMPLE_DATE_FORMAT.format(new Date()));
            System.out.println("insert " + size + " myBatis batch cost: " + (end - start) + " ms");

        } catch (Exception e) {
            System.out.println(getStackTrace(e));
        } finally {
            sqlSession.rollback();
            sqlSession.close();
        }

    }

    private static void insertBatchBySql(Integer size, Integer batchSize) {


        SqlSession sqlSession = null;
        List<InstrumentInfo> instrumentInfos = new ArrayList<InstrumentInfo>();

        try {

            //sqlSession = getSqlSessionFactory().openSession(ExecutorType.SIMPLE, Boolean.FALSE);
            sqlSession = getSqlSessionFactory().openSession(ExecutorType.BATCH, Boolean.FALSE);
            InstrumentInfoMapper instrumentInfoMapper = sqlSession.getMapper(InstrumentInfoMapper.class);
            InstrumentInfo instrumentInfoSource = instrumentInfoMapper.selectByPrimaryKey("111709220", "SPT_BD", "X_CNBD");

            initInstrumentListInfo(instrumentInfos, size, instrumentInfoSource);

            Long start = System.currentTimeMillis();
            System.out.println("insert " + size + " sql batch start : " + SIMPLE_DATE_FORMAT.format(new Date()));
            List<List<InstrumentInfo>> subList = splitList2SubList(instrumentInfos, 1000);
            for (List<InstrumentInfo> element : subList) {
                instrumentInfoMapper.insertBatch(element);
                //sqlSession.commit();
                //sqlSession.clearCache();
            }
            Long end = System.currentTimeMillis();
            System.out.println("insert " + size + " sql batch end : " + SIMPLE_DATE_FORMAT.format(new Date()));
            System.out.println("insert " + size + " sql batch cost: " + (end - start) + " ms");

        } catch (Exception e) {
            System.out.println(getStackTrace(e));
        } finally {
            sqlSession.rollback();
            sqlSession.close();
        }

    }

    public static String getStackTrace(Throwable aThrowable) {
        final Writer result = new StringWriter();
        final PrintWriter printWriter = new PrintWriter(result);
        aThrowable.printStackTrace(printWriter);
        return result.toString();
    }

    public static <T> List<List<T>> splitList2SubList(List<T> list, Integer pageSize) {

        List<List<T>>  subList = new ArrayList<List<T>>();
        List<T> childList = new ArrayList<T>();
        if (null == list) {
            return new ArrayList<List<T>>();
        }
        if (list.size() <= pageSize) {
            subList.add(list);
            return subList;
        }
        for (int i = 0 ; i < list.size(); i++) {
            childList.add(list.get(i));
            if (i != 0 && i % pageSize == 0) {
                subList.add(childList);
                childList = new ArrayList<T>();
                continue;
            }
            if (i == (list.size()-1)) {
                subList.add(childList);
            }
        }
        return subList;
    }

}
